package goxele

import (
	"fmt"
	"strconv"
	"time"
	"unsafe"
)

type TradePro struct {
	*Trade
	LocalID uint32
	ch      chan int
}

func NewTradePro() *TradePro {
	p := &TradePro{}
	p.ch = make(chan int)

	p.Trade = NewTrade()
	p.OnStart = func(errorCode int, isFirstTime bool) {
		p.ch <- errorCode
	}
	p.OnLogin = func(errorCode, exchangeCount int) {
		if errorCode != 0 {
			p.ch <- errorCode
		} else {
			fmt.Println("登录成功, 交易所数量: ", exchangeCount)
		}
	}
	p.OnLoadFinished = func(account *XTFAccount) {
		if p.OnAccount != nil {
			p.OnAccount(0, 0, account)
		}
		p.LocalID = uint32(account.LastLocalOrderID)
		p.ch <- 0
	}
	return p
}

func (t *TradePro) ReqConnect(ip string, portTrade int, portQuote int) error {
	t.SetConfig("TRADE_SERVER_IP", ip)
	t.SetConfig("TRADE_SERVER_PORT", strconv.Itoa(portTrade))
	t.SetConfig("QUERY_SERVER_IP", ip)
	t.SetConfig("QUERY_SERVER_PORT", strconv.Itoa(portQuote))
	var res int
	go func() { // 需放在 goroutine 中,否则会卡住
		res = t.Start()
		if res != 0 {
			t.ch <- res
		}
	}()
	select {
	case res = <-t.ch:
		if res != 0 {
			return fmt.Errorf("start 响应: %d", res)
		}
	case <-time.NewTimer(5 * time.Second).C:
		return fmt.Errorf("start 超时 5s")
	}
	return nil
}

func (t *TradePro) ReqLogin(accountID, password, appID, authCode string) error {
	t.SetConfig("ACCOUNT_ID", accountID)
	t.SetConfig("ACCOUNT_PWD", password)
	t.SetConfig("APP_ID", appID)
	t.SetConfig("AUTH_CODE", authCode)

	res := t.Login()
	if res != 0 {
		return fmt.Errorf("login error: %d", res)
	}

	select {
	case res = <-t.ch:
		if res != 0 {
			return fmt.Errorf("start 响应: %d", res)
		}
	case <-time.NewTimer(5 * time.Second).C:
		return fmt.Errorf("start 超时 5s")
	}
	return nil
}

func (t *TradePro) Release() {
	t.Logout()
	t.Stop()
	t.Trade = nil
}

// PrepareInstrument 为委托准备合约
//
//	@receiver t
//	@param instrumentID 合约ID
func (t *TradePro) PrepareInstrument(instrumentID string) {
	t.Instruments[instrumentID] = t.GetInstrumentByID(instrumentID)
}

// reqOrder 下单
//
//	@receiver t
//	@param instrumentID
//	@param direct
//	@param offset
//	@param price
//	@param volume
//	@param orderType
//	@param orderFlag
func (t *TradePro) reqOrder(instrumentID string, direct XTFDirection, offset XTFOffsetFlag, price float64, volume int, orderType XTFOrderType, orderFlag XTFOrderFlag) (int32, error) {
	t.LocalID += 1
	instrument, ok := t.Instruments[instrumentID]
	if !ok {
		instrument = t.GetInstrumentByID(instrumentID)
		t.Instruments[instrumentID] = instrument
	}
	res := t.InsertOrder(instrument, XTFInputOrder{
		LocalOrderID:         Uint32{}.FromUint32(t.LocalID),
		Direction:            direct,
		OffsetFlag:           offset,
		OrderType:            orderType,
		OrderPrice:           Float{}.FromFloat64(price),
		OrderVolume:          Uint32{}.FromUint32(uint32(volume)),
		ChannelSelectionType: XTF_CS_Auto,
		OrderFlag:            orderFlag,
	})
	if res != 0 {
		return int32(t.LocalID), fmt.Errorf("%d:%s", res, t.GetErrorMessage(res))
	}
	return int32(t.LocalID), nil
}

// ReqOrderInsert 限价单
//
//	@receiver t
//	@param instrumentID
//	@param direct
//	@param offset
//	@param price
//	@param volume
//	@return int
func (t *TradePro) ReqOrderInsert(instrumentID string, direct XTFDirection, offset XTFOffsetFlag, price float64, volume int) (int32, error) {
	return t.reqOrder(instrumentID, direct, offset, price, volume, XTF_ODT_Limit, XTF_ODF_Normal)
}

// ReqOrderInsertFAK 部成撤单
//
//	@receiver t
//	@param instrumentID
//	@param direct
//	@param offset
//	@param price
//	@param volume
//	@return int
func (t *TradePro) ReqOrderInsertFAK(instrumentID string, direct XTFDirection, offset XTFOffsetFlag, price float64, volume int) (int32, error) {
	return t.reqOrder(instrumentID, direct, offset, price, volume, XTF_ODT_FAK, XTF_ODF_Normal)
}

// ReqOrderInsertFAK2 合约用指针(避免map错误)
//
//	@receiver t
//	@param instrument
//	@param direct
//	@param offset
//	@param price
//	@param volume
//	@return int32
//	@return error
func (t *TradePro) ReqOrderInsertFAK2(instrument unsafe.Pointer, direct XTFDirection, offset XTFOffsetFlag, price float64, volume int) (int32, error) {
	t.LocalID += 1
	res := t.InsertOrder(instrument, XTFInputOrder{
		LocalOrderID:         Uint32{}.FromUint32(t.LocalID),
		Direction:            direct,
		OffsetFlag:           offset,
		OrderType:            XTF_ODT_FAK,
		OrderPrice:           Float{}.FromFloat64(price),
		OrderVolume:          Uint32{}.FromUint32(uint32(volume)),
		ChannelSelectionType: XTF_CS_Auto,
		OrderFlag:            XTF_ODF_Normal,
	})
	if res != 0 {
		return int32(t.LocalID), fmt.Errorf("%d:%s", res, t.GetErrorMessage(res))
	}
	return int32(t.LocalID), nil
}

// ReqOrderInsertFOK 全成全撤
//
//	@receiver t
//	@param instrumentID
//	@param direct
//	@param offset
//	@param price
//	@param volume
//	@return int
func (t *TradePro) ReqOrderInsertFOK(instrumentID string, direct XTFDirection, offset XTFOffsetFlag, price float64, volume int) (int32, error) {
	return t.reqOrder(instrumentID, direct, offset, price, volume, XTF_ODT_FOK, XTF_ODF_Normal)
}

// ReqOrderInsertWarm 预热单
//
//	@receiver t
//	@param instrumentID
//	@param direct
//	@param offset
//	@param price
//	@param volume
//	@return int
func (t *TradePro) ReqOrderInsertWarm(instrumentID string, direct XTFDirection, offset XTFOffsetFlag, price float64, volume int) (int32, error) {
	return t.reqOrder(instrumentID, direct, offset, price, volume, XTF_ODT_FAK, XTF_ODF_Warm)
}

func (t *TradePro) ReqCancelOrder() int {
	return t.CancelOrder(int32(t.LocalID))
}
